昨天談到利用委派把函數最為另一個函數的參數,不知道大家有沒有注意到一見事情,當透過委派傳遞的時候,函數的內容其實並不馬上被執行。當我們傳遞委派的時候,目的是希望將某一種操作跟另外一些操作組合起來,而高階函數的用途在於,假設我們想要做一件事情(大題目),這件事情可以被拆成許許多多的步驟(小題目),當我們定義好這些步驟後,可以把HOF當成膠水一樣把步驟串連起來,使用過Linq語法就知道,透過Linq可以把一連串的操作組合起來,先Join再Where又Select把程式串的很長。
什麼情況下會用到輸出函數的函數呢?我們可以先看一個範例
public bool ApplyInsurance(long insuranceId, string name, Datetime date, string place ......)
{
// do something...
// 申請成功後會回傳true
}
這個方法的參數超級無敵長的,通常看到這種程式碼真的很頭大,實際上我們應該會使用建造者模式,把參數一個一個設定好後執行,但其實也可以透過委派套娃封裝這個方法。
// Builder Pattern
var insurance = new Insurance();
var result = insurance
.SetId(insuranceId)
.SetName(name)
.SetApplyDate(date)
.Apply();
// 宣告委派
Func<long, Func<string, Func<DateTime, bool>>> currying1 =
id => name => date => ApplyInsurance(id,name,date);
var result = currying1(insuranceId)(name)(date);
// 或是
var currying2 = currying1(insuranceId);
var currying3 = currying2(name);
var result = currying3(date)
// 使用nuget Curryfy
var insurance = ApplyInsurance;
var curry = insurance.Curry();
var result = curry(insuranceId)(name)(date)
有沒有注意到我宣告了一個很詭異的委派,Func裡面再包Func,跟俄羅斯娃娃一樣,透過這樣的封裝我就可以讓方法一次只傳入一個參數,在還沒傳入全部的參數前,回傳的結果就是一個待執行的函數,這層封裝就是柯里化(Currying)。在函數式語言裡面原生支援柯里化,而C#的型別推斷沒辦法直接辨識所以需要明確宣告,不過每次使用都要這樣宣告真的很麻煩,其實可以安裝nuget套件Curryfy,自動把委派柯里化。另外看一下id => name => date => ApplyInsurance(id,name,date);
,這樣的寫法運用到了closure,其實在linq裡面經常被使用,這個題目擺到明天再討論吧。
第一次看到柯里化的時候,只有覺得這樣的寫法很酷炫,實在想不出這樣寫的好處是什麼。其實柯里化是一種概念,因為函數式的操作會將很多不同的方法組合,而一個方法如果有很多的參數,會造成組合上的困難,柯里化的想法是希望能夠將參數逐個擊破,以上面的的例子假設每個資料都還需要從其他地方拿到就會變成
// C#
var Apply = curry(GetId)(GetName);
不知道是不是比較能夠體會柯里化的感覺?這邊故意把Date留空是因為,這樣Apply本身就意指一個方法,而只要將最後一個參數date代入後就能得到結果。
已經講過了輸入函數以及輸出函數,針對HOF當然是可以兼而有之:
int CalculateSum(int a, int b) => a + b;
Func<T2, T1, TR> Swap<T1, T2, TR>(Func<T1, T2, TR> func)
=> (t1, t2) => func(t2, t1);
開頭有提到,HOF是作為函數單元之間的黏合劑,上面的範例裡面swap是一個轉接器,可以將函數接口的引數順序做調換,以CalculateSum為例,可以將a+b改成b+a,換句話說,如果你看某個函數的設計的讓你眼睛很痛,就可以用轉接器把接口整理成賞心悅目的格式,搭配closure的寫法,也可以將方法化簡包裝成容易使用的長相。
今天介紹了另外兩種高階函數的形式,寫到後來有點累了所內容有點潦草。總之,在函數式風格的設計下,我們會把大問題拆成若干個小問題,再透過HOF把這些小函數組裝起來,HOF是將函數組裝這個概念抽象化。突然發現我少介紹了Apply方法,明天預計討論closure,而apply等到之後要來實做題目的時候再來介紹吧。